package com.ecjtu.stadiumre_servations.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ecjtu.stadiumre_servations.config.RabbitMQConfig;
import com.ecjtu.stadiumre_servations.config.TimeConflictChecker;
import com.ecjtu.stadiumre_servations.entity.domain.Booking;
import com.ecjtu.stadiumre_servations.entity.domain.GroupActivity;
import com.ecjtu.stadiumre_servations.entity.domain.Student;
import com.ecjtu.stadiumre_servations.entity.domain.Venue;
import com.ecjtu.stadiumre_servations.entity.dto.BookingDTO;
import com.ecjtu.stadiumre_servations.mapper.*;
import com.ecjtu.stadiumre_servations.response.Result;
import com.ecjtu.stadiumre_servations.response.ResultCodeEnum;
import com.ecjtu.stadiumre_servations.service.AlipayService;
import com.ecjtu.stadiumre_servations.service.BookingService;
import com.ecjtu.stadiumre_servations.utils.LocalDateTimeSplitterUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.*;
import java.util.*;

@Service
public class BookingServiceImpl extends ServiceImpl<BookingMapper, Booking> implements BookingService {
    @Autowired
    private BookingMapper bookingMapper;
    @Autowired
    private VenueMapper venueMapper;
    @Autowired
    private StudentMapper studentMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private TimeConflictChecker timeConflictChecker;
    @Autowired
    private AlipayService alipayService;
    @Autowired
    private GroupActivityMapper groupActivityMapper;
    @Autowired
    private DataAnalysisMapper dataAnalysisMapper;
    /**
     * 查询是否被预约
     */
    @Override
    public boolean hasBookings(String venueId) {
        //根据场馆ID查询预约
        LambdaQueryWrapper<Booking> lambdaQueryWrapper = Wrappers.<Booking>lambdaQuery()
                .eq(Booking::getVenueId, venueId);
        List<Booking> bookings = bookingMapper.selectList(lambdaQueryWrapper);
        return !bookings.isEmpty();
    }
    /**
     * 创建预约
     */
    @Override
    public Result createBookings(BookingDTO bookingDTO) {
        // 1.1 查询场地信息
        Venue venue = venueMapper.selectOne(
                Wrappers.<Venue>lambdaQuery()
                        .eq(Venue::getVenueId, bookingDTO.getVenueId())
        );
        // 1.2 检查场地是否存在
        if (venue == null) {
            return Result.fail("场地不存在");
        }

        // 2.1 查询学生
        LambdaQueryWrapper<Student> studentQuery = Wrappers.<Student>lambdaQuery()
                .eq(Student::getStudentId, bookingDTO.getStudentId());
        Student student = studentMapper.selectOne(studentQuery);
        // 2.2 检查学生是否存在
        if (student == null) {
            return Result.fail("预约失败，该学生未注册");
        }

        //3.1 获取当前时间和场地开放时间段
        LocalDateTime currentTime = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        LocalDateTime venueStart = venue.getVenueStartTime();
        LocalDateTime venueEnd = venue.getVenueEndTime();

        // 3.2 检查是否在场地开放时间段内
        if (bookingDTO.getBookingStartTime().toLocalTime().isBefore(venueStart.toLocalTime()) ||
                bookingDTO.getBookingEndTime().toLocalTime().isAfter(venueEnd.toLocalTime())) {
            return Result.fail("预约失败，不在场地可预约时间段内");
        }

        // 3.3 检查预约时间是否合理（开始时间 < 结束时间 且 开始时间 > 当前时间 结束时间<闭馆时间 开始时间>开馆时间）
        if (bookingDTO.getBookingStartTime().isAfter(bookingDTO.getBookingEndTime()) ||
                bookingDTO.getBookingStartTime().isBefore(currentTime) ||
                bookingDTO.getBookingEndTime().toLocalTime().isAfter(venue.getVenueEndTime().toLocalTime()) ||
                bookingDTO.getBookingStartTime().toLocalTime().isBefore(venue.getVenueStartTime().toLocalTime())
        ) {
            return Result.fail("预约失败，预约时间不合理");
        }

        // 3.4 检查该同学在该时间段是否有其他预约
        LambdaQueryWrapper<Booking> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Booking::getStudentId, student.getStudentId());
        List<Booking> bookingList = bookingMapper.selectList(lambdaQueryWrapper);
        for (Booking booking : bookingList) {
            if (
                    timeConflictChecker.timeRangeOverlap(bookingDTO.getBookingStartTime(), bookingDTO.getBookingEndTime(),
                            booking.getBookingStartTime(), booking.getBookingEndTime())
                    && !booking.getBookingStatus().equals("已取消") //若预约取消则无视
            ) {
                return Result.fail("该同学在该时间段有其他预约");
            }
        }
        // 4.1 查询该场地是否已有预约
        LambdaQueryWrapper<Booking> bookingQuery = Wrappers.<Booking>lambdaQuery()
                .eq(Booking::getVenueId, bookingDTO.getVenueId());
        List<Booking> existingBookings = bookingMapper.selectList(bookingQuery);
        // 4.2 检查是否与已有预约冲突
        for (Booking existing : existingBookings) {
            // 若场馆预约被取消，则跳过
            if (existing.getBookingStatus().equals("已取消")||existing.getBookingStatus().equals("已使用"))
                continue;
            // 新预约开始时间 < 已有预约结束时间 且 新预约结束时间 > 已有预约开始时间
            if (timeConflictChecker.timeRangeOverlap(bookingDTO.getBookingStartTime(), bookingDTO.getBookingEndTime(),
                    existing.getBookingStartTime(), existing.getBookingEndTime())) {
                return Result.fail("预约失败，该时间段已存在预约");
            }
        }
        // 5. 预约检定成功
        Booking booking = new Booking();
        BeanUtils.copyProperties(bookingDTO, booking);
        booking.setBookingType("个人预约");
        booking.setBookingId(UUID.randomUUID().toString());
        // 持续时间
        Duration duration = Duration.between(bookingDTO.getBookingStartTime(), bookingDTO.getBookingEndTime());
        long totalMinutes = duration.toMinutes();
        long totalHours = totalMinutes / 60;
        booking.setBookingTotalPrice(totalHours * venue.getVenuePrice());
        booking.setCreateTime(LocalDateTime.now());
        booking.setBookingStatus("待支付");
        // 保存预约和待支付ID
        dataAnalysisMapper.saveForUpdate(booking.getBookingId());
        save(booking);

        // 6. 准备支付宝支付参数
        Map<String, String> alipayParams = new HashMap<>();
        alipayParams.put("out_trade_no", booking.getBookingId()); // 使用预约ID作为外部交易号
        alipayParams.put("total_amount", String.format("%.2f", booking.getBookingTotalPrice())); // 支付金额
        alipayParams.put("order_name", venue.getVenueName() + "预约支付"); // 订单名称
        alipayParams.put("body", "场地预约：" +
                "开始时间=" + bookingDTO.getBookingStartTime() +
                ",结束时间=" + bookingDTO.getBookingEndTime()); // 订单描述
        // 发送延迟消息到延迟队列（用于30分钟未支付自动取消）
        Map<String, String> timeoutMessage = new HashMap<>();
        timeoutMessage.put("bookingId", booking.getBookingId());
        try {
            rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME,
                    RabbitMQConfig.PAYMENT_TIMEOUT_DELAYED_ROUTING_KEY, timeoutMessage);
        } catch (Exception e) {
            // 发送延迟消息失败，可根据实际情况处理，如记录日志等
            log.error("发送延迟消息到RabbitMQ失败", e);
        }

        // 返回预约信息给前端
        Map<String, Object> resultData = new HashMap<>();
        resultData.put("booking", booking);
        return Result.ok(resultData);
    }
    /**
     * 查询所有预约
     */
    @Override
    public Result bookings(Page<Booking> page) {
        return Result.build(bookingMapper.selectPage(page,null), ResultCodeEnum.SELECT_SUCCESS);
    }
    /**
     * 根据场地ID查询预约
     */
    @Override
    public Result selectBookingsById(Page<Booking> page, String id) {
        LambdaQueryWrapper<Booking> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Booking::getVenueId, id);
        return Result.build(bookingMapper.selectPage(page, lambdaQueryWrapper), ResultCodeEnum.SELECT_SUCCESS);
    }
    /**
     * 根据用户id查询预约
     *
     * */
    @Override
    public Result selectBookingsByUserId(Page<Booking> page, String userId) {
        LambdaQueryWrapper<Booking> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Booking::getStudentId, userId)
                .eq(Booking::getBookingType,"个人预约");
        return Result.build(bookingMapper.selectPage(page, lambdaQueryWrapper), ResultCodeEnum.SELECT_SUCCESS);
    }
    /**
     * 取消预约
     */
    @Override
    public Result cancelBooking(String bookingId) {
    LambdaQueryWrapper<Booking> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.eq(Booking::getBookingId, bookingId);
    Booking booking = bookingMapper.selectOne(lambdaQueryWrapper);
    LocalDateTime bookingStartTime = booking.getBookingStartTime();
    LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
    if (bookingStartTime.isBefore(now)){
        return  Result.fail("该预约不可取消");
    }
    if (now.isAfter(bookingStartTime.plusHours(2))) {
        // 超时2小时，扣除部分费用
    }
    // 检查预约状态
    if ("已支付".equals(booking.getBookingStatus())) {
        // 调用支付宝退款接口
        try {
            Map<String, String> refundParams = new HashMap<>();
            refundParams.put("out_trade_no", booking.getBookingId()); // 外部交易号
            refundParams.put("refund_amount", String.format("%.2f", booking.getBookingTotalPrice())); // 退款金额
            alipayService.refund(refundParams);
        } catch (Exception e) {
            log.error("支付宝退款调用失败", e);
            return Result.fail("退款失败，请稍后再试");
        }
    }
    // 将状态更改为 已取消
    LambdaUpdateWrapper<Booking> cancelBooking = Wrappers.<Booking>lambdaUpdate()
            .set(Booking::getBookingStatus, "已取消")
            .eq(Booking::getBookingId, bookingId);
    update(cancelBooking);
    //从待更改ID中移除
    dataAnalysisMapper.deleteForUpdate(booking.getBookingId());
    return Result.ok("取消预约成功");
}

    @Override
    public Result selectBookingByBookingId(Page<Booking>page,String bookingId) {
        LambdaQueryWrapper<Booking> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.like(Booking::getBookingId, bookingId);
        Page<Booking> bookingPage = bookingMapper.selectPage(page, lambdaQueryWrapper);
        if (bookingPage == null) {
            return Result.fail("不存在预约id为" + bookingId + "的预约");
        }
        return Result.ok(bookingPage);
    }
    /**
     * 包场
     */
    @Override
    @Transactional
    public Result bookingTheEntireVenue(BookingDTO bookingDTO, String venueName) {
        // 根据名字查询场馆
        LambdaQueryWrapper<Venue> venueLambdaQueryWrapper = Wrappers.<Venue>lambdaQuery().eq(Venue::getVenueName,venueName);
        List<Venue> venues = venueMapper.selectList(venueLambdaQueryWrapper);
        // 判断要预约的整个大场馆是否有某些场馆存在预约，如果存在预约则返回:包场失败,该场馆存在预约 并将事务回滚
        // 如果不存在预约,保存预约记录
        for (Venue v :venues){
            if (!v.getVenueStatus().equals("可预约")){
                throw new RuntimeException("包场失败,该场馆存在预约");
            }else
            {
                Booking booking = new Booking();
                BeanUtils.copyProperties(bookingDTO,booking);
                booking.setBookingId(UUID.randomUUID().toString());
                // 持续时间
                Duration duration = Duration.between(bookingDTO.getBookingStartTime(), bookingDTO.getBookingEndTime());
                long totalMinutes = duration.toMinutes();
                long totalHours = totalMinutes/60;
                booking.setBookingTotalPrice(totalHours*v.getVenuePrice());
                booking.setCreateTime(LocalDateTime.now());
                booking.setBookingStatus("待支付");
                booking.setBookingType("包场");
                // 保存预约
                save(booking);
            }
        }
        return Result.ok("包场成功");
    }

    // 获取场馆的空闲时间段
    public List<LocalDateTime[]> getAvailableTimeSlots(String venueId) {
        // 查询场馆信息
        LambdaQueryWrapper<Venue>lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Venue::getVenueId,venueId);
        Venue venue = venueMapper.selectOne(lambdaQueryWrapper);
        if (venue == null) {
            return null;
        }

        // 获取场馆开放时间
        LocalDateTime venueStart = venue.getVenueStartTime();
        LocalDateTime venueEnd = venue.getVenueEndTime();


        // 获取今天的日期
        LocalDate today = LocalDate.now();
        int currentYear = today.getYear();
        int currentMonth = today.getMonthValue();
        int currentDay = today.getDayOfMonth();

        // 替换年、月、日部分
        LocalDateTime start = venueStart
                .withYear(currentYear)
                .withMonth(currentMonth)
                .withDayOfMonth(currentDay);

        LocalDateTime end = venueEnd
                .withYear(currentYear)
                .withMonth(currentMonth)
                .withDayOfMonth(currentDay);
        System.out.println(start);
        System.out.println(end);
        // 查询该场馆的所有预约记录
        LambdaQueryWrapper<Booking> queryWrapper = Wrappers.<Booking>lambdaQuery()
                .eq(Booking::getVenueId, venueId);
//                .in(Booking::getBookingStatus, "待支付", "已支付","拼团中");
        List<Booking> bookings = bookingMapper.selectList(queryWrapper);
        return calculateAvailableTimeSlots(start, end, bookings);
    }



    // 计算空闲时间段的方法
        public List<LocalDateTime[]> calculateAvailableTimeSlots(LocalDateTime venueStart, LocalDateTime venueEnd, List<Booking> bookings) {
            List<LocalDateTime[]> availableSlots = new ArrayList<>();
            LocalDateTime currentStart = venueStart;
            // 按预约开始时间排序
            bookings.sort(Comparator.comparing(Booking::getBookingStartTime));
            for (Booking booking : bookings) {
                LocalDateTime bookingStart = booking.getBookingStartTime();
                LocalDateTime bookingEnd = booking.getBookingEndTime();
                if (currentStart.isBefore(bookingStart)) {
                    // 使用工具类按小时拆分可用时间段
                    availableSlots.addAll(LocalDateTimeSplitterUtils.splitIntoHourlyIntervals(currentStart, bookingStart));
                }
                currentStart = bookingEnd.isAfter(currentStart) ? bookingEnd : currentStart;
            }
            if (currentStart.isBefore(venueEnd)) {
                // 使用工具类按小时拆分最后一段可用时间段
                availableSlots.addAll(LocalDateTimeSplitterUtils.splitIntoHourlyIntervals(currentStart, venueEnd));
            }
            return availableSlots;
        }

}
